package io.jenkins.plugins.sample;

import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractProject;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.FormValidation;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.ServletException;
import jenkins.tasks.SimpleBuildStep;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;

public class JenkinsDeployRancherBuilder extends Builder implements SimpleBuildStep {

    private final String apiToken;
    private final String image;
    private final String workload;
    private final String authorization;

    @DataBoundConstructor
    public JenkinsDeployRancherBuilder(String apiToken, String image, String workload) {
        checkParam(apiToken);
        checkParam(image);
        checkParam(workload);
        this.apiToken = apiToken.trim();
        this.image = image.trim();
        this.workload = workload.trim();
        this.authorization = getBasicAuthHeader();
    }

    private void checkParam(String paramValue) {
        if (paramValue == null || paramValue.trim().length() == 0) {
            throw new RuntimeException("parameters is empty!");
        }
    }

    private String getBasicAuthHeader() {
        if (apiToken == null || apiToken.trim().length() == 0) {
            return "";
        }
        byte[] encodedAuth = Base64.getEncoder().encode(apiToken.trim().getBytes(StandardCharsets.UTF_8));
        return "Basic " + new String(encodedAuth, StandardCharsets.UTF_8);
    }

    // 信任所有证书的方法
    private void trustAllCertificates() throws NoSuchAlgorithmException, KeyManagementException {
        TrustManager[] trustAllCerts = new TrustManager[] {
            new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkClientTrusted(X509Certificate[] certs, String authType) {}

                public void checkServerTrusted(X509Certificate[] certs, String authType) {}
            }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        HostnameVerifier allHostsValid = (hostname, session) -> true;
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    }

    @Override
    public void perform(Run<?, ?> run, FilePath workspace, EnvVars env, Launcher launcher, TaskListener listener)
            throws InterruptedException, IOException {
        listener.getLogger().println(String.format("start deploy %s to %s", image, workload));

        // 获取老的配置
        String oldConfigString = sendGet(listener.getLogger(), workload);
        JSONObject oldConfigJson = JSONObject.fromObject(oldConfigString);
        JSONArray containers = oldConfigJson.getJSONArray("containers");
        if (containers == null || containers.isEmpty()) {
            throw new RuntimeException("can not get containers");
        }
        for (int i = 0; i < containers.size(); i++) {
            JSONObject configJson = containers.getJSONObject(i);
            configJson.put("image", image);
        }
        sendPut(listener.getLogger(), workload, oldConfigJson.toString());
    }

    private String sendGet(PrintStream logger, String url) throws IOException {
        try {
            trustAllCertificates();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            throw new RuntimeException(e);
        }
        URL obj = new URL(url);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();

        // 设置请求方法为 GET
        con.setRequestMethod("GET");

        // 设置请求头
        con.setRequestProperty("Authorization", authorization);

        int responseCode = con.getResponseCode();
        logger.println("send GET to URL : " + url);
        logger.println("responseCode : " + responseCode);

        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuilder response = new StringBuilder();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        return response.toString();
    }

    private String sendPut(PrintStream logger, String url, String urlParameters) throws IOException {
        try {
            trustAllCertificates();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            throw new RuntimeException(e);
        }
        URL obj = new URL(url);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();

        // 设置请求方法为 PUT
        con.setRequestMethod("PUT");

        // 设置请求头
        con.setRequestProperty("Authorization", authorization);
        con.setRequestProperty("Content-Type", "application/json");

        // 发送 POST 请求必须设置如下两行
        con.setDoOutput(true);
        con.setDoInput(true);

        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.writeBytes(urlParameters);
        wr.flush();
        wr.close();

        int responseCode = con.getResponseCode();
        logger.println("send PUT to URL : " + url);
        logger.println("PUT params : " + urlParameters);
        logger.println("responseCode : " + responseCode);

        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuilder response = new StringBuilder();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        return response.toString();
    }

    @Symbol("jenkinsDeployRancher")
    @Extension
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {

        public FormValidation doCheckParams(@QueryParameter String value) throws IOException, ServletException {
            System.out.println("doCheckParams:" + value);
            return FormValidation.ok();
        }

        @Override
        public boolean isApplicable(Class<? extends AbstractProject> aClass) {
            System.out.println("isApplicable:" + aClass.getName());
            return true;
        }

        @Override
        public String getDisplayName() {
            return Messages.JenkinsDeployRancherBuilder_DescriptorImpl_DisplayName();
        }
    }
}
